#ifndef UTILS_H
#define UTILS_H

#include <string>
#include <fstream>
#include <sstream>

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "btBulletDynamicsCommon.h"
#include "btBulletCollisionCommon.h"

#include "Core/log.h"

namespace CC
{
    #define CCPI (3.1415926f)
    typedef float* floatPtr;
    typedef unsigned short* uShortPtr;

    class Time
    {
    public:
        static float GetTime();
    };

    class FileLog
    {
    public:
        static void WriteFile(std::string filepath, std::string data)
        {
            std::fstream fout;
            fout.open(filepath, std::ios::out);
            if (!fout.is_open())
            {
                printf("cound open file %s\n", filepath.c_str());
                return;
            }

            fout << data;
            fout.close();
        }

        static std::string ReadFile(const std::string &filepath)
        {
            CC_CORE_INFO("ReadFile filepath=%s\n", filepath.c_str());
            std::string result;
            std::ifstream in(filepath, std::ios::in);

            if (in)
            {
                //in.exceptions(std::ifstream::failbit | std::ifstream::badbit);
                in.seekg(0, std::ios::end);
                size_t size = (size_t)in.tellg();
                if (size != -1)
                {
                    result.resize(size);
                    in.seekg(0, std::ios::beg);
                    in.read(&result[0], size);

                    // CC_CORE_INFO("%d %s %s\n", size, filepath.c_str(), result.c_str());
                }
                else
                {
                    CC_CORE_WARN("Could not read from file = %s\n", filepath.c_str());
                }
            }
            else
            {
                CC_CORE_WARN("Could not open file  = %s\n", filepath.c_str());
            }

            return result;
        }

        static void ReadFile(std::string file, std::vector<std::string> &data)
        {

            std::fstream fin;
            fin.open(file);
            if (!fin.is_open())
            {
                CC_CORE_WARN("cound open file %s\n", file.c_str());
                return;
            }

            std::string line;
            while (std::getline(fin, line))
            {
                data.push_back(line);
            }
            fin.close();
        }

        static void SplitStr(std::string src, std::string pattern, std::vector<std::string> &list)
        {
            int pos = 0;
            int len = src.size();
            for (int i = 0; i < len;)
            {
                pos = src.find(pattern, i);
                if (pos != std::string::npos)
                {
                    std::string temp = src.substr(i, pos - i);
                    list.push_back(temp);
                    i = pos + 1;
                    continue;
                }
                else
                {
                    list.push_back(src.substr(i, len - i));
                    break;
                }
            }
        }

        static void SplitWStr(std::wstring src, std::wstring pattern, std::vector<std::wstring> &list)
        {
            int pos = 0;
            int len = src.size();
            for (int i = 0; i < len;)
            {
                pos = src.find(pattern, i);
                if (pos != std::wstring::npos)
                {
                    std::wstring temp = src.substr(i, pos - i);
                    list.push_back(temp);
                    i = pos + 1;
                    continue;
                }
                else
                {
                    list.push_back(src.substr(i, len - i));
                    break;
                }
            }
        }

        static void Utf8chr(int cp, char c[5])
        {
            // 一个unicode最多占5个字节。
            // char c[5]={ 0x00,0x00,0x00,0x00,0x00 };
            CC_CORE_INFO("Utf8chr cp=%d\n", cp);
            if (cp <= 0x7F)
            { // 127的常规字符
                c[0] = cp;
            }
            else if (cp <= 0x7FF)
            {
                c[0] = (cp >> 6) + 192;
                c[1] = (cp & 63) + 128;
                CC_CORE_INFO("input char width 2=%x %x\n", c[0], c[1]);
            }
            else if (0xd800 <= cp && cp <= 0xdfff)
            {
                CC_CORE_INFO("input char ivialid block of utf-8\n");
                // 无效utf-8的codepoint区
            } // invalid block of utf8
            else if (cp <= 0xFFFF)
            {
                c[0] = (cp >> 12) + 224;
                c[1] = ((cp >> 6) & 63) + 128;
                c[2] = (cp & 63) + 128;
                CC_CORE_INFO("input char width 3=%x %x  %x\n", c[0], c[1], c[2]);
            }
            else if (cp <= 0x10FFFF)
            {
                c[0] = (cp >> 18) + 240;
                c[1] = ((cp >> 12) & 63) + 128;
                c[2] = ((cp >> 6) & 63) + 128;
                c[3] = (cp & 63) + 128;
                CC_CORE_INFO("input char width 4\n");
            }
        }

        static void Unicode2Utf8(std::string unicodeSrc, std::string &Utf8Src){
            // 2char -> 3char
            CC_ASSERT(2 == unicodeSrc.size(), "Unicode2Utf8 unicodeSrc.size != 2\n");

            int a = (int)unicodeSrc[0] & 0xFF;
            int b = (int)unicodeSrc[1] & 0xFF;

            char c[5]={ 0x00,0x00,0x00,0x00,0x00 };
            Utf8chr(a << 8 | b, c);
            Utf8Src = std::string(c);
            CC_CORE_INFO("Unicode2Utf8 = %s ac=%x, bc=%x, a=%d,b=%d, codepoint=%ld\n", c, unicodeSrc[0], unicodeSrc[1], a, b, a << 8 | b);
        }
    };

    class Tool
    {
    public:
        static glm::mat4 RotateByLocalAngle(glm::mat4 model_mt, glm::vec3 rotation, bool localOrientation = true)
        {
            glm::mat4 model_mt_local = glm::mat4(1.0f);

            model_mt_local = glm::rotate(model_mt_local, glm::radians(rotation.x), glm::vec3(0.0, 0.0, 1.0));
            model_mt_local = glm::rotate(model_mt_local, glm::radians(rotation.y), glm::vec3(0.0, 1.0, 0.0));
            model_mt_local = glm::rotate(model_mt_local, glm::radians(rotation.z), glm::vec3(1.0, 0.0, 0.0));

            if (localOrientation)
            {
                return model_mt * model_mt_local; // local
            }
            else
            {
                return model_mt_local * model_mt; // global
            }
        }

        static glm::mat4 RotateByLocalRadians(glm::mat4 model_mt, glm::vec3 rotation, bool localOrientation = true)
        {
            glm::mat4 model_mt_local = glm::mat4(1.0f);

            model_mt_local = glm::rotate(model_mt_local, (rotation.x), glm::vec3(0.0, 0.0, 1.0));
            model_mt_local = glm::rotate(model_mt_local, (rotation.y), glm::vec3(0.0, 1.0, 0.0));
            model_mt_local = glm::rotate(model_mt_local, (rotation.z), glm::vec3(1.0, 0.0, 0.0));

            if (localOrientation)
            {
                return model_mt * model_mt_local; // local
            }
            else
            {
                return model_mt_local * model_mt; // global
            }
        }

        static glm::mat4 TranslateByLocal(glm::mat4 model_mt, glm::vec3 pos, bool localOrientation = true)
        {
            glm::mat4 model_mt_local = glm::mat4(1.0f);
            model_mt_local = glm::translate(model_mt_local, pos);

            if (localOrientation)
            {
                return model_mt * model_mt_local; // local
            }
            else
            {
                return model_mt_local * model_mt; // global
            }
        }

        static glm::mat4 ScaleByLocal(glm::mat4 model_mt, glm::vec3 scale, bool localOrientation = true)
        {
            glm::mat4 model_mt_local = glm::mat4(1.0f);
            model_mt_local = glm::scale(model_mt_local, scale);

            if (localOrientation)
            {
                return model_mt * model_mt_local; // local
            }
            else
            {
                return model_mt_local * model_mt; // global
            }
        }

        static std::string UInt2Str(unsigned int v)
        {
            std::stringstream stream;
            stream << v;
            std::string str = stream.str();
            return str;
        }

        static std::string Int2Str(int v)
        {
            std::stringstream stream;
            stream << v;
            std::string str = stream.str();
            return str;
        }

        static std::string Int2StrZero(int v, int length)
        {
            std::stringstream stream;
            stream << v;
            std::string str = stream.str();

            while (str.size() < length) {
                str = "0" + str;
            }
            return str;
        }

        static std::string Float2Str(float v)
        {
            std::stringstream stream;
            stream << v;
            std::string str = stream.str();
            return str;
        }

        static std::string uint64_t2Str(uint64_t v) {
            std::stringstream stream;
            stream << v;
            std::string str = stream.str();
            return str;
        }

        static std::string Vec32Str(glm::vec3 v){
            std::string data;
            data += Float2Str(v[0]) + "," + Float2Str(v[1]) + "," + Float2Str(v[2]) + ",";
            return data;
        }

        static std::string Vec22Str(glm::vec2 v){
            std::string data;
            data += Float2Str(v[0]) + "," + Float2Str(v[1]) + ",";
            return data;
        }

        static std::string Int42Str(int v[4]){
            std::string data;
            data += Int2Str(v[0]) + "," + Int2Str(v[1]) + "," + Int2Str(v[2]) + "," + Int2Str(v[3]) + ",";
            return data;
        }

        static std::string Float42Str(float v[4]){
            std::string data;
            data += Float2Str(v[0]) + "," + Float2Str(v[1]) + "," + Float2Str(v[2]) + "," + Float2Str(v[3]) + ",";
            return data;
        }

        //
        static uint64_t Str2uint64_t(std::string v)
        {
            std::stringstream stream;
            stream << v;
            uint64_t a;
            stream >> a;
            return a;
        }

        static unsigned int Str2UInt(std::string v)
        {
            std::stringstream stream;
            stream << v;
            unsigned int a;
            stream >> a;
            return a;
        }  

        static int Str2Int(std::string v)
        {
            std::stringstream stream;
            stream << v;
            int a;
            stream >> a;
            return a;
        }  

        static float Str2Float(std::string v)
        {
            std::stringstream stream;
            stream << v;
            float a;
            stream >> a;
            return a;
        }        

        static inline glm::vec4 GetGLMVec4(btVector4 &vec)
        {
            return glm::vec4(vec[0], vec[1], vec[2], vec[3]);
        }

        static inline glm::vec3 GetGLMVec3(btVector3 &vec)
        {
            return glm::vec3(vec[0], vec[1], vec[2]);
        }

        static inline btVector3 GetVec3GLM(glm::vec3 &vec)
        {
            return btVector3(btScalar(vec[0]), btScalar(vec[1]), btScalar(vec[2]));
        }

        static inline btVector4 GetVec4GLM(glm::vec4 &vec)
        {
            return btVector4(btScalar(vec[0]), btScalar(vec[1]), btScalar(vec[2]), btScalar(vec[3]));
        }
    };
}

#endif